﻿// -----------------------------------------------------------------------
// <copyright file="BigIntegerGenerator.cs" company="none">
// Copyright (c) 2012 M. Alberti, xanatos(at)live.it
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// </copyright>
// -----------------------------------------------------------------------

namespace FluentSerializer.Generators
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.Linq.Expressions;
    using System.Numerics;
    using System.Reflection;

    /// <summary>
    /// 
    /// </summary>
    public static class BigIntegerGenerator
    {
        /// <summary>
        /// 
        /// </summary>
        private static readonly ConstructorInfo BigIntegerConstructor = typeof(BigInteger).GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(byte[]) }, null);

        /// <summary>
        /// 
        /// </summary>
        private static readonly MethodInfo BigIntegerToByteArray = typeof(BigInteger).GetMethod("ToByteArray", BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="serializer"></param>
        /// <param name="parametersType"></param>
        /// <param name="type"></param>
        /// <param name="helper"></param>
        /// <returns></returns>
        [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:ParameterMustNotSpanMultipleLines", Justification = "More readable this way")]
        public static Descriptor BuildSerializer(Serializer serializer, Type parametersType, Type type, Type helper)
        {
            bool isUniversal = true;

            Type decoratedTypeWithHelper = helper.IsGenericTypeDefinition ? helper.MakeGenericType(type) : helper;
            Type realType = Serializer.UndecorateType(type);

            if (realType != typeof(BigInteger))
            {
                throw new NotSupportedException();
            }

            Type subType = typeof(byte[]);

            Descriptor descriptor1 = Serializer.GetSerializer(serializer, subType, subType, helper);
            isUniversal &= descriptor1.IsUniversal;

            ParameterExpression item = Expression.Parameter(realType);
            ParameterExpression parameters = parametersType != null ? Expression.Parameter(parametersType) : null;
            ParameterExpression subItem = Expression.Variable(subType);

            ConstructorInfo constructor = decoratedTypeWithHelper.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
            var helper2 = (ICollectionHelper)constructor.Invoke(null);
            isUniversal &= helper2.IsUniversal;

            LambdaExpression deserializeExpression;
            LambdaExpression serializeExpression;
            LambdaExpression sizeExpression;

            /***/
            {
                var block = new List<Expression>();

                if (!(descriptor1.DefaultExpression is DefaultExpression))
                {
                    block.Add(Expression.Assign(subItem, descriptor1.DefaultExpression));
                }

                block.Add(Serializer.MakeFromLambda(descriptor1.DeserializeExpression, descriptor1.IsUniversal, Serializer.ReaderExpression, subItem, parameters));

                BlockExpression expression = Expression.Block(new[] { subItem }, block);

                deserializeExpression = Serializer.MakeLambda(Expression.New(BigIntegerConstructor, expression), descriptor1.IsUniversal, Serializer.ReaderExpression, item, parameters);
            }

            /***/
            {
                BlockExpression expression = Expression.Block(
                    new[] { subItem },
                    new Expression[] 
                    {
                        Expression.Assign(subItem, Expression.Call(item, BigIntegerToByteArray)),
                        Serializer.MakeFromLambda(descriptor1.SerializeExpression, descriptor1.IsUniversal, Serializer.WriterExpression, subItem, parameters)
                    });

                serializeExpression = Serializer.MakeLambda(expression, descriptor1.IsUniversal, Serializer.WriterExpression, item, parameters);
            }

            /***/
            {
                BlockExpression expression = Expression.Block(
                    new[] { subItem },
                    new Expression[] 
                    {
                        Expression.Assign(subItem, Expression.Call(item, BigIntegerToByteArray)),
                        Serializer.MakeFromLambda(descriptor1.SizeExpression, descriptor1.IsUniversal, subItem, parameters)
                    });

                sizeExpression = Serializer.MakeLambda(expression, descriptor1.IsUniversal, item, parameters);
            }

            var descriptor2 = new Descriptor(decoratedTypeWithHelper, isUniversal, deserializeExpression, DeserializationHandling.RequireAssignment, serializeExpression, sizeExpression);
            return descriptor2;
        }
    }
}